package com.centersoft.flowable.core.web.service;

import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSONArray;
import com.centersoft.flowable.core.config.WorkFlowConfig;
import com.centersoft.flowable.core.constant.FlowableConstant;
import com.centersoft.flowable.core.dto.TaskAuditDto;
import com.centersoft.flowable.core.dto.TaskBackDto;
import com.centersoft.flowable.core.dto.TaskHandlerDto;
import com.centersoft.flowable.core.dto.TaskStopDto;
import com.centersoft.flowable.core.exception.FlowableRuntimeException;
import com.centersoft.flowable.core.gateway.UserInterface;
import com.centersoft.flowable.core.model.*;
import com.centersoft.flowable.core.model.*;
import com.centersoft.flowable.core.util.FlowableUtils;
import com.centersoft.flowable.core.web.cmd.BackUserTaskCmd;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.runtime.ActivityInstance;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wangzhiqiang
 * @since 2025/3/12 16:35
 */
@Slf4j
@Service
public class FlowTaskService {

    private final UserInterface userInterface;


    private final FlowTaskClientService flowTaskClientService;

    private final WorkFlowConfig workFlowConfig;


    private final RuntimeService runtimeService;
    private final TaskService taskService;
    private final RepositoryService repositoryService;

    private final ManagementService managementService;

    public FlowTaskService(UserInterface userInterface, FlowTaskClientService flowTaskClientService, WorkFlowConfig workFlowConfig, RuntimeService runtimeService, TaskService taskService, RepositoryService repositoryService, ManagementService managementService) {
        this.userInterface = userInterface;
        this.flowTaskClientService = flowTaskClientService;
        this.workFlowConfig = workFlowConfig;
        this.runtimeService = runtimeService;
        this.taskService = taskService;
        this.repositoryService = repositoryService;
        this.managementService = managementService;
    }

    public List<String> getUserByRoleId(QueryUserByRoleIdRequest request) {
        return userInterface.queryUserListByRoleId(request);
    }

    public List<String> getUserByDepId(QueryUserByDepIdRequest request) {
        return userInterface.queryUserListByDeptId(request);
    }

    @Transactional
    public void startProcess(StartProcessRequest reviewRequest) {
        reviewRequest.setApplyUserId(userInterface.getCurrentUser().getUserId());
        flowTaskClientService.startProcessForClient(reviewRequest);
    }



    public QueryCanAuditResult getUserCanAudit(String procInsId) {
        MyFlowableUser currentUser = userInterface.getCurrentUser();
        if (currentUser == null) {
            log.warn("[flowable]:current user is null");
            currentUser = new MyFlowableUser();
        }
        try {
            //通过flowButtons判断是否是审核人
            Boolean isAudit = flowTaskClientService.isMyAudit(procInsId, currentUser.getUserId());
            Boolean isCanReCall = flowTaskClientService.isCanRecall(procInsId, currentUser.getUserId());
            Map<String, Object> flowData = new HashMap<>();
            flowData.put("isAudit", isAudit);
            flowData.put("isCanRecall", isCanReCall);
            return new QueryCanAuditResult().setIsAudit(isAudit).setIsCanRecall(isCanReCall).setProInsId(procInsId);
        } catch (Exception e) {
            return new QueryCanAuditResult().setIsAudit(false).setIsCanRecall(false).setProInsId(procInsId);
        }
    }

    public JSONArray getAuditListByEmid(String menuCode, String emid) {
        List<ProcessVo> auditListByEmid1 = getAuditListByEmid1(menuCode, emid);
        return (JSONArray)JSONArray.toJSON(auditListByEmid1);
    }

    public List<ProcessVo> getAuditListByEmid1(String menuCode, String emid) {
        QueryTodoListRequest request = new QueryTodoListRequest();
        if (StringUtils.isNotBlank(menuCode)) {
            request.setMenuCodes(Collections.singletonList(menuCode));
        }
        if (StringUtils.isNotBlank(emid)) {
            request.setUserId(emid);
        }

        return flowTaskClientService.todoList(request);

    }

    public JSONArray getAppAuditListByEmid(String menuCode, String emid) {
        List<ProcessVo> auditListByEmid1 = getAppAuditListByEmid1(menuCode, emid);
        return (JSONArray)JSONArray.toJSON(auditListByEmid1);
    }

    public List<ProcessVo> getAppAuditListByEmid1(String menuCode, String emid) {
        QueryTodoListRequest request = new QueryTodoListRequest();
        if (StringUtils.isNotBlank(menuCode)) {
            request.setMenuCodes(Collections.singletonList(menuCode));
        } else {
            request.setMenuCodes(Arrays.asList(workFlowConfig.getAppMenuCode().split("[,;]]")));
        }
        if (StringUtils.isNotBlank(emid)) {
            request.setUserId(emid);
        }

        return flowTaskClientService.todoList(request);
    }

    public Set<String> getAuditBillcodes(String menuCode) {
        List<ProcessVo> auditListByEmid = this.getAuditListByEmid1(menuCode, userInterface.getCurrentUser().getUserId());
        return auditListByEmid.stream().map(ProcessVo::getBillcode).collect(Collectors.toSet());
    }


    public void delHistoryDataByProcInsId(String procInsId) {
        flowTaskClientService.delHistoryDataByProcInsId(procInsId);
    }

    /**
     * 审批
     *
     * @param
     */
    @Transactional
    public void agree(TaskAuditDto agreeDTo) {
        Task task = taskService.createTaskQuery().taskId(agreeDTo.getTaskId()).singleResult();
        Assert.notNull(task, "找不到可审核的任务");

        String processInstanceId = task.getProcessInstanceId();

        Map<String, Object> variables = runtimeService.getVariables(processInstanceId);

        complete(task, agreeDTo, variables);
    }

    /**
     * 提交任务, 并保存意见
     *
     * @param vars 任务变量
     */
    @Transactional(readOnly = false)
    public void complete(Task task, TaskAuditDto agreeDTo, Map<String, Object> vars) {

        String processInsId = task.getProcessInstanceId();

        // 添加意见
        taskService.addComment(
                task.getId(),
                processInsId,
                agreeDTo.getCommentType(),
                agreeDTo.getMessage());

        // 设置流程变量
        if (vars == null) {
            vars = new HashMap<>();
        }


        setCurrentApproved(task, agreeDTo);
        // owner不为空说明可能存在委托任务
        if (StringUtils.isNotBlank(task.getOwner())) {
            DelegationState delegationState = task.getDelegationState();
            switch (delegationState) {
                case PENDING:
                    taskService.resolveTask(task.getId());
                    taskService.complete(task.getId(), vars);
                    break;

                case RESOLVED:
                    // 委托任务已经完成
                    break;

                default:
                    // 不是委托任务
                    taskService.complete(task.getId(), vars);
                    break;
            }
        } else if (StringUtils.isBlank(task.getAssignee())) { // 未签收任务
            // 签收任务
            taskService.claim(task.getId(), agreeDTo.getAssignee());
            // 提交任务
            taskService.complete(task.getId(), vars);
        } else {
            // 提交任务
            taskService.complete(task.getId(), vars);
        }
    }

    @Transactional
    public void stop(TaskStopDto request) {
        stopProcessInstanceById(request, ProcessStatus.STOP);
    }

    /**
     * NB- BN------------------------------------------------------------------------------------------------------------------------------------------------------BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB1终止流程实例9O--+
     *
     * @param request 流程实例ID
     */
    @SneakyThrows
    @Transactional(readOnly = false)
    public void stopProcessInstanceById(TaskStopDto request, ProcessStatus processStatus) {
        Task task =null;
        String procInsId = request.getProcInsId();
        if (StringUtils.isNotBlank(request.getTaskId())){
            task = taskService.createTaskQuery().taskId(request.getTaskId()).singleResult();
            Assert.notNull(task, "找不到可审核的任务");
            procInsId = task.getProcessInstanceId();
        }else {
            task = taskService.createTaskQuery().processInstanceId(request.getProcInsId()).list().get(0);
        }





        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(procInsId).singleResult();
        ActionType actionType = null;
        if (processInstance != null) {
            //1、添加审批记录
            if (processStatus == ProcessStatus.REVOKE) {
                actionType = ActionType.REVOKE;
            } else if (processStatus == ProcessStatus.STOP) {
                actionType = ActionType.STOP;
            } else if (processStatus == ProcessStatus.REJECT) {
                actionType = ActionType.REJECT;
            } else if (processStatus == ProcessStatus.DELETED) {
                actionType = ActionType.DELETED;
            }


            taskService.addComment(task.getId(), procInsId, actionType.getType(), request.getMessage());
            if (StringUtils.isBlank(task.getAssignee())) { // 未签收任务
                taskService.claim(task.getId(), request.getAssignee());
            }
            setCurrentApproved(task, request);
            runtimeService.setVariable(procInsId, FlowableConstant.PROCESS_STATUS_CODE, processStatus.getCode());

            List<EndEvent> endNodes = findEndFlowElement(processInstance.getProcessDefinitionId());
            String endId = endNodes.get(0).getId();
            //2、执行终止40
            List<Execution> executions = runtimeService.createExecutionQuery().parentId(procInsId).list();
            List<String> executionIds = new ArrayList<>();
            executions.forEach(execution -> executionIds.add(execution.getId()));
            this.moveExecutionsToSingleActivityId(executionIds, endId);
        }
    }

    @Transactional
    public void back(TaskBackDto backDTo) {
        List<FlowNode> nodes = getBackNodes(backDTo.getTaskId());
        // 先查询前置节点是否可以驳回
        if (CollectionUtils.isEmpty(nodes)) {
            throw new FlowableRuntimeException("未获取到可驳回的任务节点");
        }
        // 上一个节点
        FlowNode previousFlow = nodes.get(nodes.size() - 1);


        // 驳回流程
        backTask(backDTo, previousFlow);
    }

    /**
     * 获取可驳回节点
     *
     * @param taskId
     * @return
     */
    public List<FlowNode> getBackNodes(String taskId) {
        Task taskEntity = taskService.createTaskQuery().taskId(taskId).singleResult();
        String processInstanceId = taskEntity.getProcessInstanceId();
        String currActId = taskEntity.getTaskDefinitionKey();
        String processDefinitionId = taskEntity.getProcessDefinitionId();
        Process process = repositoryService.getBpmnModel(processDefinitionId).getMainProcess();
        FlowNode currentFlowElement = (FlowNode) process.getFlowElement(currActId, true);
        List<ActivityInstance> activitys = runtimeService
                .createActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .finished()
                .orderByActivityInstanceStartTime()
                .asc()
                .list();
        List<String> activityIds = activitys.stream()
                .filter(activity -> activity.getActivityType().equals(BpmnXMLConstants.ELEMENT_TASK_USER) || activity.getActivityType().equals(BpmnXMLConstants.ELEMENT_EVENT_START))
                .filter(activity -> !activity.getActivityId().equals(currActId))
                .map(ActivityInstance::getActivityId)
                .distinct()
                .collect(Collectors.toList());

        List<FlowNode> result = new ArrayList<>();
        for (String activityId : activityIds) {
            FlowNode toBackFlowElement = (FlowNode) process.getFlowElement(activityId, true);
            if (FlowableUtils.isReachable(process, toBackFlowElement, currentFlowElement)) {
                result.add(toBackFlowElement);
            }
        }
        return result;
    }

    /*
     * 驳回任务
     */
    @Transactional(readOnly = false)
    public void backTask(TaskBackDto backDTo, FlowNode previousFlow) {
        Task task = taskService.createTaskQuery().taskId(backDTo.getTaskId()).singleResult();
        if (StringUtils.isBlank(task.getAssignee())) {
            taskService.claim(backDTo.getTaskId(), backDTo.getAssignee());
        }

        // 退回发起者处理,退回到发起者,默认设置任务执行人为发起者
        ActivityInstance targetRealActivityInstance = runtimeService
                .createActivityInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .activityId(previousFlow.getId())
                .list()
                .get(0);
        setCurrentApproved(task, backDTo);
        if (targetRealActivityInstance.getActivityType().equals(BpmnXMLConstants.ELEMENT_EVENT_START)) {
            stopProcessInstanceById(TaskBackDto.toSTopDto(backDTo), ProcessStatus.REJECT);
        } else {
            taskService.addComment(backDTo.getTaskId(), task.getProcessInstanceId(), backDTo.getCommentType(), backDTo.getMessage());
            managementService.executeCommand(new BackUserTaskCmd(runtimeService, backDTo.getTaskId(), previousFlow.getId()));
        }
    }

    private void setCurrentApproved(Task task, TaskHandlerDto backDTo) {
        runtimeService.setVariable(task.getProcessInstanceId(), FlowableConstant.CURRENT_APPROVER, backDTo.getAssignee());
        runtimeService.setVariable(task.getProcessInstanceId(), FlowableConstant.APPROVER_MSG, backDTo.getMessage());
    }


    public BpmnModel getBpmnModelByProcessDefId(String processDefId) {
        return repositoryService.getBpmnModel(processDefId);
    }


    public List<EndEvent> findEndFlowElement(String processDefId) {
        BpmnModel bpmnModel = this.getBpmnModelByProcessDefId(processDefId);
        if (bpmnModel != null) {
            Process process = bpmnModel.getMainProcess();
            return process.findFlowElementsOfType(EndEvent.class);
        } else {
            return null;
        }
    }

    /**
     * 执行跳转
     */
    protected void moveExecutionsToSingleActivityId(List<String> executionIds, String activityId) {
        runtimeService.createChangeActivityStateBuilder()
                .moveExecutionsToSingleActivityId(executionIds, activityId)
                .changeState();
    }




}
